/*@Author S.Genikhov*
  Creation Date 19/06/2010
  Last modifed 19/06/2010
  @St_Schelduler run function with some repeatability
  @In this version the basis is Queue based on vector. Addition function to
  @exept standard Queue is inserd by index and PQ_Showindex were added to Queue interface
  @to allow St_shellduler rull the the queue to convert it to PQ function.
  @In next version PQ will be developed and Vector will be changed to link list.
  Addtion problems of using vector is the constant memory consumption after poprocess*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include  <unistd.h>
#include <math.h>

#include <ST_Schelduller.h>


/**********List of function excluded from h files**********************************************/

/*Support function to Sch_Schelduling from h file*/
err_Sch ST_Task_activation_and_time_updating(ST_Task* someTask);
err_Sch Sch_InsertTask_to_blockQ(PQ* _Bq, IOReq* _IOBtask);

/*****Implementation part******************************************************************/
err_Sch ST_Task_activation_and_time_updating(ST_Task* someTask)
{
    struct          timeval tv_start;
    double          present_time;

    gettimeofday(&tv_start, NULL);
    present_time=time(NULL)*pow(10,6)+tv_start.tv_usec;/*mikro sec scale*/
    if(1000*(someTask->nextrun)>present_time)
    {
        unsigned long sleep_time=(unsigned long)(1000*(someTask->nextrun)-present_time);
        int sec_sleep=sleep_time/1000000;
        int usec_sleep=sleep_time%1000000;
        sleep(sec_sleep);/*assepted sec*/
        usleep(usec_sleep);/*assepted mksec*/
        someTask->ST_func();/*executs its function*/
        present_time=time(NULL)*pow(10,6)+tv_start.tv_usec;/*mikro sec scale*/
        someTask->nextrun=((present_time+someTask->period*1000)/1000);
    }
    else
    {
     someTask->ST_func();/*executs its function*/
     /* updates present time*/
      gettimeofday(&tv_start, NULL);
      present_time=time(NULL)*pow(10,6)+tv_start.tv_usec;/*mikro sec scale*/
      someTask->nextrun=((present_time+someTask->period*1000)/1000);
    }
    return 0;
}

err_Sch Sch_InsertTask_inRightplace_to_Queue(PQ* _Que,size_t* Q_size,ST_Task* someTask)
{
    size_t      i;
    ST_Task*    toCompareTask;

    for(i=0;i<(*Q_size);++i)/*insertion inside*/
    {
        PQ_Showindex(_Que,(void**)&toCompareTask,i);
        if(toCompareTask->nextrun>someTask->nextrun)
        {
            Queue_Push_to_index(_Que,(void*)someTask,i);
            ++*Q_size;
            break;
        }
    }
    if(i== *Q_size)
    {
        PQ_Queue_Pushback(_Que,someTask);
      ++*Q_size;/*Adding to end of list*/
    }
    return 0;
}

err_Sch Sch_Schelduling(Sch_Schel* Sch)
{
    PQ_Queue_Popfront(Sch->readyQ,(void**)&Sch->running);/*Get the data*/
    --Sch->readyQ_size;
#ifdef _Debug_of_Schelduller
    if(Sch->running->userdata!=0)/*That is not a driver*/
    {
        var_t* testtemp=(var_t*)(Sch->running->userdata);
     /*   printf("\n ST_schelduling run :\n fdnumber of running is% d \n if_file_open% d ",testtemp->fdnumber,testtemp->if_file_open);*/
    }
#endif/*_Debug_of_Schelduller*/
    ST_Task_activation_and_time_updating(Sch->running);

    /*if running task has the 'ready status after activation it goes to readyQ
    otherwise to blockQ*/
    if(Sch->running->is_reschdeldular==ready)
    {
            Sch_InsertTask_inRightplace_to_Queue(Sch->readyQ,&(Sch->readyQ_size),Sch->running);
    }
    return 0;
}


err_Sch Sch_InsertTask_to_blockQ(PQ* _Bq, IOReq* _IOBtask)
{
    if (_IOBtask==0)
    {
        return NOT_ACCEPTED_BLOCKQ;
        }

    if(_IOBtask==0)
    {
        return NOT_ACCEPTED_BTASK;
    }
    PQ_Queue_Pushback(_Bq,_IOBtask);
    return 0;
}



/**********************************ACTIVATION OF System************************************************/
void ST_FreeOneTask(ST_Task* Task)
{
    free(Task->userdata);
    MM_Destroy(Task->MM_Man);
    free(Task->MM_Man);
#ifdef _Debug_of_Schelduller
    printf("\n FREE address of Task->MM_Man is      %x \n", Task->MM_Man);
#endif     /*_Debug_of_Scheldulle */

}

void ST_RemoveandDeleteallTask(Sch_Schel* _Sch)
{
    IOReq*       tempIOreq;
     /*Remove task task from readyQ*/
     while(_Sch->readyQ_size)
     {
         PQ_Queue_Popfront(_Sch->readyQ,(void**)&_Sch->running);/*Get the data*/
         --_Sch->readyQ_size;
         ST_FreeOneTask(_Sch->running);
     }
     /*Remove task task from BlockQ*/
     while(_Sch->blockQ_size)
     {
         PQ_Queue_Popfront(_Sch->blockQ,OUT (void**) &tempIOreq);
        --_Sch->blockQ_size;
        ST_FreeOneTask(tempIOreq->Task);
     }
}


void Sch_Destroy(Sch_Schel* _Sch)
{
    ST_RemoveandDeleteallTask(_Sch);
    PQ_Queue_Destroy(_Sch->readyQ);
    PQ_Queue_Destroy(_Sch->blockQ);
    free(_Sch);
    return;
}

err_Sch Sch_InsertTask(Sch_Schel* _Sch,func workfunc,size_t mperiod,void* _userdata )
{
    err_Sch       err;
    err_MM        err1;
    size_t       i;
    struct       timeval tv_start;
    double       present_time;/*mikro sec*/

    MMheap*      Memman=MM_Create(MM_HEAP_SIZE,MEM_UNIT);
    if(Memman==0)
    {
        return ST_MEMMANAGER_MEM_ALLOCATION_PROBLEMS;
    }

    ST_Task* oneTask=MM_Malloc(Memman,sizeof(ST_Task),INOUT  &err1);

    if(oneTask==0 || err1)
    {
        return ST_TASK_KREATION_PROBLEMS;
    }

    oneTask->period              =mperiod;
    oneTask->ST_func             =workfunc;
    (oneTask->userdata)          =_userdata;
    oneTask->is_reschdeldular    =ready;
    oneTask->MM_Man              =Memman;/*Defined on Memory Manager*/
    oneTask->taskID                  =1000+_Sch->PIDofTask;
    /*oneTask->FD defined on stack pointers to list of filedescriptors*/
    #ifdef _Debug_of_Schelduller
    if(_userdata!=0)
    {
        var_t* testtemp=(var_t*)(oneTask->userdata);
        if(testtemp->fdnumber==-1 && testtemp->if_file_open==closed)
        {
            printf("\n Kernel_initdata and user data insertion o.k.");
        }
        else
        {exit(ST_TASK_INSTERTION_PROBLEMS);}
    }
    #endif/*_Debug_of_Schelduller*/

     /*intitilaizing the list of fd*/
     for(i=0;i<sizeFD;++i)
     {
         oneTask->FD[i]=0;
     }
    gettimeofday(&tv_start, NULL);/*mikro second scale*/
    present_time=time(NULL)*pow(10,6)+tv_start.tv_usec;
    oneTask->nextrun=((present_time+oneTask->period*1000)/1000);/*convert to msec*/
    err=(err_Sch)PQ_Queue_Pushback(_Sch->readyQ,oneTask);
    if(err)
    {
        return ST_BLOCKQ_INSERTION_PROBLEMS;
    }
     ++_Sch->readyQ_size;
     ++_Sch->PIDofTask;
    return 0;
}

err_Sch Sch_Initiate(Sch_Schel* ST,func DRIVER,size_t DRIVER_PERIOD)/*need add errors*/
{

    ST->readyQ      =PQ_Creat();/*Ready que creation*/
    ST->blockQ      =PQ_Creat();/*Blocking Que qreation*/
    ST->STfunc      =Sch_Schelduling;
    Sch_InsertTask(ST,DRIVER,DRIVER_PERIOD,NULL);/*void* data fild field of driver is NULL*/
    ST->readyQ_size=1;
    ST->blockQ_size=0;
    ST->PIDofTask=0;
    return 0;
}

#ifdef unitest_of_scheduller
int main()
{
     Sch_Initiate();
     return 0;
}
#endif /*unitest_of_scheduller*/

